home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / share / gettext / intl / lock.c < prev    next >
Encoding:
C/C++ Source or Header  |  2007-03-05  |  22.1 KB  |  923 lines

  1. /* Locking in multithreaded situations.
  2.    Copyright (C) 2005-2006 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify it
  5.    under the terms of the GNU Library General Public License as published
  6.    by the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12.    Library General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU Library General Public
  15.    License along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  17.    USA.  */
  18.  
  19. /* Written by Bruno Haible <bruno@clisp.org>, 2005.
  20.    Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h,
  21.    gthr-win32.h.  */
  22.  
  23. #include <config.h>
  24.  
  25. #include "lock.h"
  26.  
  27. /* ========================================================================= */
  28.  
  29. #if USE_POSIX_THREADS
  30.  
  31. /* Use the POSIX threads library.  */
  32.  
  33. # if PTHREAD_IN_USE_DETECTION_HARD
  34.  
  35. /* The function to be executed by a dummy thread.  */
  36. static void *
  37. dummy_thread_func (void *arg)
  38. {
  39.   return arg;
  40. }
  41.  
  42. int
  43. glthread_in_use (void)
  44. {
  45.   static int tested;
  46.   static int result; /* 1: linked with -lpthread, 0: only with libc */
  47.  
  48.   if (!tested)
  49.     {
  50.       pthread_t thread;
  51.  
  52.       if (pthread_create (&thread, NULL, dummy_thread_func, NULL) != 0)
  53.     /* Thread creation failed.  */
  54.     result = 0;
  55.       else
  56.     {
  57.       /* Thread creation works.  */
  58.       void *retval;
  59.       if (pthread_join (thread, &retval) != 0)
  60.         abort ();
  61.       result = 1;
  62.     }
  63.       tested = 1;
  64.     }
  65.   return result;
  66. }
  67.  
  68. # endif
  69.  
  70. /* -------------------------- gl_lock_t datatype -------------------------- */
  71.  
  72. /* ------------------------- gl_rwlock_t datatype ------------------------- */
  73.  
  74. # if HAVE_PTHREAD_RWLOCK
  75.  
  76. #  if !defined PTHREAD_RWLOCK_INITIALIZER
  77.  
  78. void
  79. glthread_rwlock_init (gl_rwlock_t *lock)
  80. {
  81.   if (pthread_rwlock_init (&lock->rwlock, NULL) != 0)
  82.     abort ();
  83.   lock->initialized = 1;
  84. }
  85.  
  86. void
  87. glthread_rwlock_rdlock (gl_rwlock_t *lock)
  88. {
  89.   if (!lock->initialized)
  90.     {
  91.       if (pthread_mutex_lock (&lock->guard) != 0)
  92.     abort ();
  93.       if (!lock->initialized)
  94.     glthread_rwlock_init (lock);
  95.       if (pthread_mutex_unlock (&lock->guard) != 0)
  96.     abort ();
  97.     }
  98.   if (pthread_rwlock_rdlock (&lock->rwlock) != 0)
  99.     abort ();
  100. }
  101.  
  102. void
  103. glthread_rwlock_wrlock (gl_rwlock_t *lock)
  104. {
  105.   if (!lock->initialized)
  106.     {
  107.       if (pthread_mutex_lock (&lock->guard) != 0)
  108.     abort ();
  109.       if (!lock->initialized)
  110.     glthread_rwlock_init (lock);
  111.       if (pthread_mutex_unlock (&lock->guard) != 0)
  112.     abort ();
  113.     }
  114.   if (pthread_rwlock_wrlock (&lock->rwlock) != 0)
  115.     abort ();
  116. }
  117.  
  118. void
  119. glthread_rwlock_unlock (gl_rwlock_t *lock)
  120. {
  121.   if (!lock->initialized)
  122.     abort ();
  123.   if (pthread_rwlock_unlock (&lock->rwlock) != 0)
  124.     abort ();
  125. }
  126.  
  127. void
  128. glthread_rwlock_destroy (gl_rwlock_t *lock)
  129. {
  130.   if (!lock->initialized)
  131.     abort ();
  132.   if (pthread_rwlock_destroy (&lock->rwlock) != 0)
  133.     abort ();
  134.   lock->initialized = 0;
  135. }
  136.  
  137. #  endif
  138.  
  139. # else
  140.  
  141. void
  142. glthread_rwlock_init (gl_rwlock_t *lock)
  143. {
  144.   if (pthread_mutex_init (&lock->lock, NULL) != 0)
  145.     abort ();
  146.   if (pthread_cond_init (&lock->waiting_readers, NULL) != 0)
  147.     abort ();
  148.   if (pthread_cond_init (&lock->waiting_writers, NULL) != 0)
  149.     abort ();
  150.   lock->waiting_writers_count = 0;
  151.   lock->runcount = 0;
  152. }
  153.  
  154. void
  155. glthread_rwlock_rdlock (gl_rwlock_t *lock)
  156. {
  157.   if (pthread_mutex_lock (&lock->lock) != 0)
  158.     abort ();
  159.   /* Test whether only readers are currently running, and whether the runcount
  160.      field will not overflow.  */
  161.   /* POSIX says: "It is implementation-defined whether the calling thread
  162.      acquires the lock when a writer does not hold the lock and there are
  163.      writers blocked on the lock."  Let's say, no: give the writers a higher
  164.      priority.  */
  165.   while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
  166.     {
  167.       /* This thread has to wait for a while.  Enqueue it among the
  168.      waiting_readers.  */
  169.       if (pthread_cond_wait (&lock->waiting_readers, &lock->lock) != 0)
  170.     abort ();
  171.     }
  172.   lock->runcount++;
  173.   if (pthread_mutex_unlock (&lock->lock) != 0)
  174.     abort ();
  175. }
  176.  
  177. void
  178. glthread_rwlock_wrlock (gl_rwlock_t *lock)
  179. {
  180.   if (pthread_mutex_lock (&lock->lock) != 0)
  181.     abort ();
  182.   /* Test whether no readers or writers are currently running.  */
  183.   while (!(lock->runcount == 0))
  184.     {
  185.       /* This thread has to wait for a while.  Enqueue it among the
  186.      waiting_writers.  */
  187.       lock->waiting_writers_count++;
  188.       if (pthread_cond_wait (&lock->waiting_writers, &lock->lock) != 0)
  189.     abort ();
  190.       lock->waiting_writers_count--;
  191.     }
  192.   lock->runcount--; /* runcount becomes -1 */
  193.   if (pthread_mutex_unlock (&lock->lock) != 0)
  194.     abort ();
  195. }
  196.  
  197. void
  198. glthread_rwlock_unlock (gl_rwlock_t *lock)
  199. {
  200.   if (pthread_mutex_lock (&lock->lock) != 0)
  201.     abort ();
  202.   if (lock->runcount < 0)
  203.     {
  204.       /* Drop a writer lock.  */
  205.       if (!(lock->runcount == -1))
  206.     abort ();
  207.       lock->runcount = 0;
  208.     }
  209.   else
  210.     {
  211.       /* Drop a reader lock.  */
  212.       if (!(lock->runcount > 0))
  213.     abort ();
  214.       lock->runcount--;
  215.     }
  216.   if (lock->runcount == 0)
  217.     {
  218.       /* POSIX recommends that "write locks shall take precedence over read
  219.      locks", to avoid "writer starvation".  */
  220.       if (lock->waiting_writers_count > 0)
  221.     {
  222.       /* Wake up one of the waiting writers.  */
  223.       if (pthread_cond_signal (&lock->waiting_writers) != 0)
  224.         abort ();
  225.     }
  226.       else
  227.     {
  228.       /* Wake up all waiting readers.  */
  229.       if (pthread_cond_broadcast (&lock->waiting_readers) != 0)
  230.         abort ();
  231.     }
  232.     }
  233.   if (pthread_mutex_unlock (&lock->lock) != 0)
  234.     abort ();
  235. }
  236.  
  237. void
  238. glthread_rwlock_destroy (gl_rwlock_t *lock)
  239. {
  240.   if (pthread_mutex_destroy (&lock->lock) != 0)
  241.     abort ();
  242.   if (pthread_cond_destroy (&lock->waiting_readers) != 0)
  243.     abort ();
  244.   if (pthread_cond_destroy (&lock->waiting_writers) != 0)
  245.     abort ();
  246. }
  247.  
  248. # endif
  249.  
  250. /* --------------------- gl_recursive_lock_t datatype --------------------- */
  251.  
  252. # if HAVE_PTHREAD_MUTEX_RECURSIVE
  253.  
  254. #  if !(defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
  255.  
  256. void
  257. glthread_recursive_lock_init (gl_recursive_lock_t *lock)
  258. {
  259.   pthread_mutexattr_t attributes;
  260.  
  261.   if (pthread_mutexattr_init (&attributes) != 0)
  262.     abort ();
  263.   if (pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE) != 0)
  264.     abort ();
  265.   if (pthread_mutex_init (&lock->recmutex, &attributes) != 0)
  266.     abort ();
  267.   if (pthread_mutexattr_destroy (&attributes) != 0)
  268.     abort ();
  269.   lock->initialized = 1;
  270. }
  271.  
  272. void
  273. glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
  274. {
  275.   if (!lock->initialized)
  276.     {
  277.       if (pthread_mutex_lock (&lock->guard) != 0)
  278.     abort ();
  279.       if (!lock->initialized)
  280.     glthread_recursive_lock_init (lock);
  281.       if (pthread_mutex_unlock (&lock->guard) != 0)
  282.     abort ();
  283.     }
  284.   if (pthread_mutex_lock (&lock->recmutex) != 0)
  285.     abort ();
  286. }
  287.  
  288. void
  289. glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
  290. {
  291.   if (!lock->initialized)
  292.     abort ();
  293.   if (pthread_mutex_unlock (&lock->recmutex) != 0)
  294.     abort ();
  295. }
  296.  
  297. void
  298. glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
  299. {
  300.   if (!lock->initialized)
  301.     abort ();
  302.   if (pthread_mutex_destroy (&lock->recmutex) != 0)
  303.     abort ();
  304.   lock->initialized = 0;
  305. }
  306.  
  307. #  endif
  308.  
  309. # else
  310.  
  311. void
  312. glthread_recursive_lock_init (gl_recursive_lock_t *lock)
  313. {
  314.   if (pthread_mutex_init (&lock->mutex, NULL) != 0)
  315.     abort ();
  316.   lock->owner = (pthread_t) 0;
  317.   lock->depth = 0;
  318. }
  319.  
  320. void
  321. glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
  322. {
  323.   pthread_t self = pthread_self ();
  324.   if (lock->owner != self)
  325.     {
  326.       if (pthread_mutex_lock (&lock->mutex) != 0)
  327.     abort ();
  328.       lock->owner = self;
  329.     }
  330.   if (++(lock->depth) == 0) /* wraparound? */
  331.     abort ();
  332. }
  333.  
  334. void
  335. glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
  336. {
  337.   if (lock->owner != pthread_self ())
  338.     abort ();
  339.   if (lock->depth == 0)
  340.     abort ();
  341.   if (--(lock->depth) == 0)
  342.     {
  343.       lock->owner = (pthread_t) 0;
  344.       if (pthread_mutex_unlock (&lock->mutex) != 0)
  345.     abort ();
  346.     }
  347. }
  348.  
  349. void
  350. glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
  351. {
  352.   if (lock->owner != (pthread_t) 0)
  353.     abort ();
  354.   if (pthread_mutex_destroy (&lock->mutex) != 0)
  355.     abort ();
  356. }
  357.  
  358. # endif
  359.  
  360. /* -------------------------- gl_once_t datatype -------------------------- */
  361.  
  362. static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
  363.  
  364. int
  365. glthread_once_singlethreaded (pthread_once_t *once_control)
  366. {
  367.   /* We don't know whether pthread_once_t is an integer type, a floating-point
  368.      type, a pointer type, or a structure type.  */
  369.   char *firstbyte = (char *)once_control;
  370.   if (*firstbyte == *(const char *)&fresh_once)
  371.     {
  372.       /* First time use of once_control.  Invert the first byte.  */
  373.       *firstbyte = ~ *(const char *)&fresh_once;
  374.       return 1;
  375.     }
  376.   else
  377.     return 0;
  378. }
  379.  
  380. #endif
  381.  
  382. /* ========================================================================= */
  383.  
  384. #if USE_PTH_THREADS
  385.  
  386. /* Use the GNU Pth threads library.  */
  387.  
  388. /* -------------------------- gl_lock_t datatype -------------------------- */
  389.  
  390. /* ------------------------- gl_rwlock_t datatype ------------------------- */
  391.  
  392. /* --------------------- gl_recursive_lock_t datatype --------------------- */
  393.  
  394. /* -------------------------- gl_once_t datatype -------------------------- */
  395.  
  396. void
  397. glthread_once_call (void *arg)
  398. {
  399.   void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
  400.   void (*initfunction) (void) = *gl_once_temp_addr;
  401.   initfunction ();
  402. }
  403.  
  404. int
  405. glthread_once_singlethreaded (pth_once_t *once_control)
  406. {
  407.   /* We know that pth_once_t is an integer type.  */
  408.   if (*once_control == PTH_ONCE_INIT)
  409.     {
  410.       /* First time use of once_control.  Invert the marker.  */
  411.       *once_control = ~ PTH_ONCE_INIT;
  412.       return 1;
  413.     }
  414.   else
  415.     return 0;
  416. }
  417.  
  418. #endif
  419.  
  420. /* ========================================================================= */
  421.  
  422. #if USE_SOLARIS_THREADS
  423.  
  424. /* Use the old Solaris threads library.  */
  425.  
  426. /* -------------------------- gl_lock_t datatype -------------------------- */
  427.  
  428. /* ------------------------- gl_rwlock_t datatype ------------------------- */
  429.  
  430. /* --------------------- gl_recursive_lock_t datatype --------------------- */
  431.  
  432. void
  433. glthread_recursive_lock_init (gl_recursive_lock_t *lock)
  434. {
  435.   if (mutex_init (&lock->mutex, USYNC_THREAD, NULL) != 0)
  436.     abort ();
  437.   lock->owner = (thread_t) 0;
  438.   lock->depth = 0;
  439. }
  440.  
  441. void
  442. glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
  443. {
  444.   thread_t self = thr_self ();
  445.   if (lock->owner != self)
  446.     {
  447.       if (mutex_lock (&lock->mutex) != 0)
  448.     abort ();
  449.       lock->owner = self;
  450.     }
  451.   if (++(lock->depth) == 0) /* wraparound? */
  452.     abort ();
  453. }
  454.  
  455. void
  456. glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
  457. {
  458.   if (lock->owner != thr_self ())
  459.     abort ();
  460.   if (lock->depth == 0)
  461.     abort ();
  462.   if (--(lock->depth) == 0)
  463.     {
  464.       lock->owner = (thread_t) 0;
  465.       if (mutex_unlock (&lock->mutex) != 0)
  466.     abort ();
  467.     }
  468. }
  469.  
  470. void
  471. glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
  472. {
  473.   if (lock->owner != (thread_t) 0)
  474.     abort ();
  475.   if (mutex_destroy (&lock->mutex) != 0)
  476.     abort ();
  477. }
  478.  
  479. /* -------------------------- gl_once_t datatype -------------------------- */
  480.  
  481. void
  482. glthread_once (gl_once_t *once_control, void (*initfunction) (void))
  483. {
  484.   if (!once_control->inited)
  485.     {
  486.       /* Use the mutex to guarantee that if another thread is already calling
  487.      the initfunction, this thread waits until it's finished.  */
  488.       if (mutex_lock (&once_control->mutex) != 0)
  489.     abort ();
  490.       if (!once_control->inited)
  491.     {
  492.       once_control->inited = 1;
  493.       initfunction ();
  494.     }
  495.       if (mutex_unlock (&once_control->mutex) != 0)
  496.     abort ();
  497.     }
  498. }
  499.  
  500. int
  501. glthread_once_singlethreaded (gl_once_t *once_control)
  502. {
  503.   /* We know that gl_once_t contains an integer type.  */
  504.   if (!once_control->inited)
  505.     {
  506.       /* First time use of once_control.  Invert the marker.  */
  507.       once_control->inited = ~ 0;
  508.       return 1;
  509.     }
  510.   else
  511.     return 0;
  512. }
  513.  
  514. #endif
  515.  
  516. /* ========================================================================= */
  517.  
  518. #if USE_WIN32_THREADS
  519.  
  520. /* -------------------------- gl_lock_t datatype -------------------------- */
  521.  
  522. void
  523. glthread_lock_init (gl_lock_t *lock)
  524. {
  525.   InitializeCriticalSection (&lock->lock);
  526.   lock->guard.done = 1;
  527. }
  528.  
  529. void
  530. glthread_lock_lock (gl_lock_t *lock)
  531. {
  532.   if (!lock->guard.done)
  533.     {
  534.       if (InterlockedIncrement (&lock->guard.started) == 0)
  535.     /* This thread is the first one to need this lock.  Initialize it.  */
  536.     glthread_lock_init (lock);
  537.       else
  538.     /* Yield the CPU while waiting for another thread to finish
  539.        initializing this lock.  */
  540.     while (!lock->guard.done)
  541.       Sleep (0);
  542.     }
  543.   EnterCriticalSection (&lock->lock);
  544. }
  545.  
  546. void
  547. glthread_lock_unlock (gl_lock_t *lock)
  548. {
  549.   if (!lock->guard.done)
  550.     abort ();
  551.   LeaveCriticalSection (&lock->lock);
  552. }
  553.  
  554. void
  555. glthread_lock_destroy (gl_lock_t *lock)
  556. {
  557.   if (!lock->guard.done)
  558.     abort ();
  559.   DeleteCriticalSection (&lock->lock);
  560.   lock->guard.done = 0;
  561. }
  562.  
  563. /* ------------------------- gl_rwlock_t datatype ------------------------- */
  564.  
  565. static inline void
  566. gl_waitqueue_init (gl_waitqueue_t *wq)
  567. {
  568.   wq->array = NULL;
  569.   wq->count = 0;
  570.   wq->alloc = 0;
  571.   wq->offset = 0;
  572. }
  573.  
  574. /* Enqueues the current thread, represented by an event, in a wait queue.
  575.    Returns INVALID_HANDLE_VALUE if an allocation failure occurs.  */
  576. static HANDLE
  577. gl_waitqueue_add (gl_waitqueue_t *wq)
  578. {
  579.   HANDLE event;
  580.   unsigned int index;
  581.  
  582.   if (wq->count == wq->alloc)
  583.     {
  584.       unsigned int new_alloc = 2 * wq->alloc + 1;
  585.       HANDLE *new_array =
  586.     (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
  587.       if (new_array == NULL)
  588.     /* No more memory.  */
  589.     return INVALID_HANDLE_VALUE;
  590.       /* Now is a good opportunity to rotate the array so that its contents
  591.      starts at offset 0.  */
  592.       if (wq->offset > 0)
  593.     {
  594.       unsigned int old_count = wq->count;
  595.       unsigned int old_alloc = wq->alloc;
  596.       unsigned int old_offset = wq->offset;
  597.       unsigned int i;
  598.       if (old_offset + old_count > old_alloc)
  599.         {
  600.           unsigned int limit = old_offset + old_count - old_alloc;
  601.           for (i = 0; i < limit; i++)
  602.         new_array[old_alloc + i] = new_array[i];
  603.         }
  604.       for (i = 0; i < old_count; i++)
  605.         new_array[i] = new_array[old_offset + i];
  606.       wq->offset = 0;
  607.     }
  608.       wq->array = new_array;
  609.       wq->alloc = new_alloc;
  610.     }
  611.   event = CreateEvent (NULL, TRUE, FALSE, NULL);
  612.   if (event == INVALID_HANDLE_VALUE)
  613.     /* No way to allocate an event.  */
  614.     return INVALID_HANDLE_VALUE;
  615.   index = wq->offset + wq->count;
  616.   if (index >= wq->alloc)
  617.     index -= wq->alloc;
  618.   wq->array[index] = event;
  619.   wq->count++;
  620.   return event;
  621. }
  622.  
  623. /* Notifies the first thread from a wait queue and dequeues it.  */
  624. static inline void
  625. gl_waitqueue_notify_first (gl_waitqueue_t *wq)
  626. {
  627.   SetEvent (wq->array[wq->offset + 0]);
  628.   wq->offset++;
  629.   wq->count--;
  630.   if (wq->count == 0 || wq->offset == wq->alloc)
  631.     wq->offset = 0;
  632. }
  633.  
  634. /* Notifies all threads from a wait queue and dequeues them all.  */
  635. static inline void
  636. gl_waitqueue_notify_all (gl_waitqueue_t *wq)
  637. {
  638.   unsigned int i;
  639.  
  640.   for (i = 0; i < wq->count; i++)
  641.     {
  642.       unsigned int index = wq->offset + i;
  643.       if (index >= wq->alloc)
  644.     index -= wq->alloc;
  645.       SetEvent (wq->array[index]);
  646.     }
  647.   wq->count = 0;
  648.   wq->offset = 0;
  649. }
  650.  
  651. void
  652. glthread_rwlock_init (gl_rwlock_t *lock)
  653. {
  654.   InitializeCriticalSection (&lock->lock);
  655.   gl_waitqueue_init (&lock->waiting_readers);
  656.   gl_waitqueue_init (&lock->waiting_writers);
  657.   lock->runcount = 0;
  658.   lock->guard.done = 1;
  659. }
  660.  
  661. void
  662. glthread_rwlock_rdlock (gl_rwlock_t *lock)
  663. {
  664.   if (!lock->guard.done)
  665.     {
  666.       if (InterlockedIncrement (&lock->guard.started) == 0)
  667.     /* This thread is the first one to need this lock.  Initialize it.  */
  668.     glthread_rwlock_init (lock);
  669.       else
  670.     /* Yield the CPU while waiting for another thread to finish
  671.        initializing this lock.  */
  672.     while (!lock->guard.done)
  673.       Sleep (0);
  674.     }
  675.   EnterCriticalSection (&lock->lock);
  676.   /* Test whether only readers are currently running, and whether the runcount
  677.      field will not overflow.  */
  678.   if (!(lock->runcount + 1 > 0))
  679.     {
  680.       /* This thread has to wait for a while.  Enqueue it among the
  681.      waiting_readers.  */
  682.       HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
  683.       if (event != INVALID_HANDLE_VALUE)
  684.     {
  685.       DWORD result;
  686.       LeaveCriticalSection (&lock->lock);
  687.       /* Wait until another thread signals this event.  */
  688.       result = WaitForSingleObject (event, INFINITE);
  689.       if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
  690.         abort ();
  691.       CloseHandle (event);
  692.       /* The thread which signalled the event already did the bookkeeping:
  693.          removed us from the waiting_readers, incremented lock->runcount.  */
  694.       if (!(lock->runcount > 0))
  695.         abort ();
  696.       return;
  697.     }
  698.       else
  699.     {
  700.       /* Allocation failure.  Weird.  */
  701.       do
  702.         {
  703.           LeaveCriticalSection (&lock->lock);
  704.           Sleep (1);
  705.           EnterCriticalSection (&lock->lock);
  706.         }
  707.       while (!(lock->runcount + 1 > 0));
  708.     }
  709.     }
  710.   lock->runcount++;
  711.   LeaveCriticalSection (&lock->lock);
  712. }
  713.  
  714. void
  715. glthread_rwlock_wrlock (gl_rwlock_t *lock)
  716. {
  717.   if (!lock->guard.done)
  718.     {
  719.       if (InterlockedIncrement (&lock->guard.started) == 0)
  720.     /* This thread is the first one to need this lock.  Initialize it.  */
  721.     glthread_rwlock_init (lock);
  722.       else
  723.     /* Yield the CPU while waiting for another thread to finish
  724.        initializing this lock.  */
  725.     while (!lock->guard.done)
  726.       Sleep (0);
  727.     }
  728.   EnterCriticalSection (&lock->lock);
  729.   /* Test whether no readers or writers are currently running.  */
  730.   if (!(lock->runcount == 0))
  731.     {
  732.       /* This thread has to wait for a while.  Enqueue it among the
  733.      waiting_writers.  */
  734.       HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
  735.       if (event != INVALID_HANDLE_VALUE)
  736.     {
  737.       DWORD result;
  738.       LeaveCriticalSection (&lock->lock);
  739.       /* Wait until another thread signals this event.  */
  740.       result = WaitForSingleObject (event, INFINITE);
  741.       if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
  742.         abort ();
  743.       CloseHandle (event);
  744.       /* The thread which signalled the event already did the bookkeeping:
  745.          removed us from the waiting_writers, set lock->runcount = -1.  */
  746.       if (!(lock->runcount == -1))
  747.         abort ();
  748.       return;
  749.     }
  750.       else
  751.     {
  752.       /* Allocation failure.  Weird.  */
  753.       do
  754.         {
  755.           LeaveCriticalSection (&lock->lock);
  756.           Sleep (1);
  757.           EnterCriticalSection (&lock->lock);
  758.         }
  759.       while (!(lock->runcount == 0));
  760.     }
  761.     }
  762.   lock->runcount--; /* runcount becomes -1 */
  763.   LeaveCriticalSection (&lock->lock);
  764. }
  765.  
  766. void
  767. glthread_rwlock_unlock (gl_rwlock_t *lock)
  768. {
  769.   if (!lock->guard.done)
  770.     abort ();
  771.   EnterCriticalSection (&lock->lock);
  772.   if (lock->runcount < 0)
  773.     {
  774.       /* Drop a writer lock.  */
  775.       if (!(lock->runcount == -1))
  776.     abort ();
  777.       lock->runcount = 0;
  778.     }
  779.   else
  780.     {
  781.       /* Drop a reader lock.  */
  782.       if (!(lock->runcount > 0))
  783.     abort ();
  784.       lock->runcount--;
  785.     }
  786.   if (lock->runcount == 0)
  787.     {
  788.       /* POSIX recommends that "write locks shall take precedence over read
  789.      locks", to avoid "writer starvation".  */
  790.       if (lock->waiting_writers.count > 0)
  791.     {
  792.       /* Wake up one of the waiting writers.  */
  793.       lock->runcount--;
  794.       gl_waitqueue_notify_first (&lock->waiting_writers);
  795.     }
  796.       else
  797.     {
  798.       /* Wake up all waiting readers.  */
  799.       lock->runcount += lock->waiting_readers.count;
  800.       gl_waitqueue_notify_all (&lock->waiting_readers);
  801.     }
  802.     }
  803.   LeaveCriticalSection (&lock->lock);
  804. }
  805.  
  806. void
  807. glthread_rwlock_destroy (gl_rwlock_t *lock)
  808. {
  809.   if (!lock->guard.done)
  810.     abort ();
  811.   if (lock->runcount != 0)
  812.     abort ();
  813.   DeleteCriticalSection (&lock->lock);
  814.   if (lock->waiting_readers.array != NULL)
  815.     free (lock->waiting_readers.array);
  816.   if (lock->waiting_writers.array != NULL)
  817.     free (lock->waiting_writers.array);
  818.   lock->guard.done = 0;
  819. }
  820.  
  821. /* --------------------- gl_recursive_lock_t datatype --------------------- */
  822.  
  823. void
  824. glthread_recursive_lock_init (gl_recursive_lock_t *lock)
  825. {
  826.   lock->owner = 0;
  827.   lock->depth = 0;
  828.   InitializeCriticalSection (&lock->lock);
  829.   lock->guard.done = 1;
  830. }
  831.  
  832. void
  833. glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
  834. {
  835.   if (!lock->guard.done)
  836.     {
  837.       if (InterlockedIncrement (&lock->guard.started) == 0)
  838.     /* This thread is the first one to need this lock.  Initialize it.  */
  839.     glthread_recursive_lock_init (lock);
  840.       else
  841.     /* Yield the CPU while waiting for another thread to finish
  842.        initializing this lock.  */
  843.     while (!lock->guard.done)
  844.       Sleep (0);
  845.     }
  846.   {
  847.     DWORD self = GetCurrentThreadId ();
  848.     if (lock->owner != self)
  849.       {
  850.     EnterCriticalSection (&lock->lock);
  851.     lock->owner = self;
  852.       }
  853.     if (++(lock->depth) == 0) /* wraparound? */
  854.       abort ();
  855.   }
  856. }
  857.  
  858. void
  859. glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
  860. {
  861.   if (lock->owner != GetCurrentThreadId ())
  862.     abort ();
  863.   if (lock->depth == 0)
  864.     abort ();
  865.   if (--(lock->depth) == 0)
  866.     {
  867.       lock->owner = 0;
  868.       LeaveCriticalSection (&lock->lock);
  869.     }
  870. }
  871.  
  872. void
  873. glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
  874. {
  875.   if (lock->owner != 0)
  876.     abort ();
  877.   DeleteCriticalSection (&lock->lock);
  878.   lock->guard.done = 0;
  879. }
  880.  
  881. /* -------------------------- gl_once_t datatype -------------------------- */
  882.  
  883. void
  884. glthread_once (gl_once_t *once_control, void (*initfunction) (void))
  885. {
  886.   if (once_control->inited <= 0)
  887.     {
  888.       if (InterlockedIncrement (&once_control->started) == 0)
  889.     {
  890.       /* This thread is the first one to come to this once_control.  */
  891.       InitializeCriticalSection (&once_control->lock);
  892.       EnterCriticalSection (&once_control->lock);
  893.       once_control->inited = 0;
  894.       initfunction ();
  895.       once_control->inited = 1;
  896.       LeaveCriticalSection (&once_control->lock);
  897.     }
  898.       else
  899.     {
  900.       /* Undo last operation.  */
  901.       InterlockedDecrement (&once_control->started);
  902.       /* Some other thread has already started the initialization.
  903.          Yield the CPU while waiting for the other thread to finish
  904.          initializing and taking the lock.  */
  905.       while (once_control->inited < 0)
  906.         Sleep (0);
  907.       if (once_control->inited <= 0)
  908.         {
  909.           /* Take the lock.  This blocks until the other thread has
  910.          finished calling the initfunction.  */
  911.           EnterCriticalSection (&once_control->lock);
  912.           LeaveCriticalSection (&once_control->lock);
  913.           if (!(once_control->inited > 0))
  914.         abort ();
  915.         }
  916.     }
  917.     }
  918. }
  919.  
  920. #endif
  921.  
  922. /* ========================================================================= */
  923.